﻿package Core 
{
	import com.junkbyte.console.Cc;	
		
	import Albums.AlbumIcon;
	import Albums.AlbumsPage;	
	import Common.Constants;
	import Common.Room;
	import Common.JPEGAsyncEncoder;
	import Common.NormalCheckbox;
	import Common.TextMessage;	
	
	import Events.ImageEvent;	
	import Network.XMLLoader;
	
	import fl.containers.ScrollPane;
	import flash.display.Stage;
	import flash.display.InteractiveObject;
	import flash.display.Shape;
	import flash.display.Sprite;	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.FileReference;
	import flash.net.FileReferenceList;
	import flash.text.TextField;
	import flash.utils.Dictionary;	
	import flash.ui.Keyboard;
	
	public class ImagesViewer extends Sprite
	{	
		private const CImageWidth:Number = 105;
		private const CImageHeight:int = 105;
		private const COffset:int = 5;		
		
		private var FImagesPerRow:int;
		private var FWidth:int;
		private var FHeight:int;
		private var FMinSelectionY:Number;
		private var FRoot:Main;
		private var FList:FileReferenceList;		
		private var FImages:Vector.<ImageContainer>;
		private var FSelection:Vector.<ImageContainer>;
		private var FCategory:int;
		private var FRestrictedClasses:Vector.<Class>;
		private var FTotal:int;
		private var FCurrent:int;
		//Interface
		private var FContainer:Sprite;		
		private var FDragContainer:DragContainer;		
		private var FTip:Tooltip;		
		private var FAlbums:AlbumsPage;
		private var FScroller:ScrollPane;
		private var FLoadingMessage:TextMessage;
		//Uploadeing local files and updating DB
		private var FUploader:ImagesUploader;
		//Pointer to interface buttons collection
		private var FButtons:Dictionary;
		//Network
		private var FLoader:XMLLoader;
		//private var FEncoder:JPEGAsyncEncoder;
		private var FShift:Shape;		
		
		public function ImagesViewer() 
		{	
			FWidth = new Number(Math.round(width));
			FHeight = new Number(Math.round(height));
			FImagesPerRow = Math.floor((FWidth - COffset) / (CImageWidth + COffset));
			//FEncoder = new JPEGAsyncEncoder(Constants.IMAGE_QUALITY);
			//FEncoder.PixelsPerIteration = 128;
			
			FImages = new Vector.<ImageContainer>;
			FSelection = new Vector.<ImageContainer>;
			
			FContainer = new Sprite();
			addChild(FContainer);
			FDragContainer = new DragContainer();
			addChild(FDragContainer);
			FTip = new Tooltip();
			addChild(FTip);
			
			FShift = new Shape();
			FShift.graphics.beginFill(0, 0);
			FShift.graphics.drawRect(0, 0, COffset, COffset);
			FShift.graphics.endFill();
			addChild(FShift);
			
			FUploader = new ImagesUploader();			
			
			FRestrictedClasses = new Vector.<Class>;
			FRestrictedClasses.push(ImageContainer);
			FRestrictedClasses.push(ImageDescriptor);
			FRestrictedClasses.push(ImageDeleter);
			FRestrictedClasses.push(NormalCheckbox);
			FRestrictedClasses.push(ImageViewer);
			
			addEventListener(Event.ADDED, OnAddedToParent);			
		}
			
		private function OnAddedToParent(e:Event):void 
		{
			if (parent.parent is ScrollPane)
			{
				KeysUtil.Register(stage);
				FScroller = ScrollPane(parent.parent);
				FMinSelectionY = FScroller.y;
				stage.addEventListener(MouseEvent.MOUSE_MOVE, OnMouseMove);
				stage.addEventListener(MouseEvent.MOUSE_DOWN, OnStageMouseDown);
				stage.addEventListener(MouseEvent.MOUSE_UP, OnStageMouseUp);
				stage.addEventListener(Event.MOUSE_LEAVE, OnStageMouseLeave);
				stage.addEventListener(MouseEvent.MOUSE_WHEEL, OnMouseWheel);
			}
		}
		
		private function OnMouseWheel(e:MouseEvent):void 
		{
			if (FScroller)			
				FScroller.verticalScrollPosition -= e.delta * 10;			
		}
		
		public function Clear():void		
		{
			for (var i in FImages)			
				FImages[i].Destroy();			
			FImages = new Vector.<ImageContainer>;
			FSelection = new Vector.<ImageContainer>;
			FContainer = new Sprite();
			FContainer.y = 0;
			addChild(FContainer);			
			addChild(FTip);
			FUploader.Container = this;
		}		
		
		private function ResetImages():void 
		{
			FSelection = new Vector.<ImageContainer>;
			while (FContainer.numChildren > 0)
				FContainer.removeChildAt(0);
		}
				
		public function Sync():void
		{
			FUploader.Sync();
		}
		
		//{ Remote images loading
		
		private function OnLoadingStarted(e:Event):void
		{
			FLoadingMessage = new TextMessage;
			FTotal = 0;
			FCurrent = 0;
			FLoadingMessage.Header = 'Идет загрузка изображений';
			FLoadingMessage.Description = 'Пожалуйста, подождите, идет загрузка файла настроек.';
			FRoot.MessagesCore.Show(FLoadingMessage);
		}
		
		private function OnXMLLoaded(e:Event):void 
		{			
			Clear();
			if(FScroller)
				FScroller.update();
			AllButtonsEnabled = false;
			var vImage:ImageContainer;			
			if (FLoader.ImagesCount > 0 )
			{
				FTotal = FLoader.ImagesCount;
				for (var i:int = 0; i < FLoader.ImagesCount; i++)
				{				
					vImage = new RemoteImage(CImageWidth, CImageHeight);				
					vImage.XMLS = FLoader;
					vImage.IsLocal = false;
					RemoteImage(vImage).Info = FLoader.ImageAt(i);
					FRoot.RegisterForViewer(vImage);
					FContainer.addChild(vImage);
					vImage.CheckUpdates = true;
					FImages.push(vImage);				
				}
				Category = FLoader.MainID;
				LoadImages();
			}
			else
			{
				FRoot.MessagesCore.visible = false;
				AllButtonsEnabled = true;				
				Category = FLoader.MainID;
			}
		}
		//} endregion
		// End of Remote images loading
		
		//{ Local images loading 
			
		public function LoadFromList(AList:FileReferenceList):Vector.<ImageContainer>
		{			
			if(FScroller)
				FScroller.update();
			AllButtonsEnabled = false;
			FList = AList;
			var vImage:ImageContainer;			
			for (var i in AList.fileList)
			{				
				vImage = new LocalImage(CImageWidth, CImageHeight);
				//LocalImage(vImage).Encoder = FEncoder;
				vImage.XMLS = FLoader;
				vImage.IsLocal = true;
				vImage.ObjectID = FCategory;
				LocalImage(vImage).File = AList.fileList[i];
				vImage.CheckUpdates = true;
				FImages.push(vImage);				
			}
			PlaceImages();
			LoadImages();
			return FImages;
		}
		
		private function PlaceImages():void
		{
			var vRows:int = 0;
			var vColumns:int = 0;
			var vOffset:Number = COffset;			
			vOffset += (FWidth - (FImagesPerRow * (CImageWidth + COffset) + COffset * 2)) / FImagesPerRow;
			var vStartOffset:Number = (vOffset + COffset) / 2;
			var vLastImage:ImageContainer = null;
			
			for (var i in FImages)
			{
				var vImage:ImageContainer = FImages[i];				
				if (vImage.ObjectID == FCategory)
				{
					if (!vImage.parent)
						FContainer.addChild(vImage);
					vImage.x = (vImage.width + vOffset) * vColumns + vStartOffset;
					vImage.y = COffset + vRows * (vImage.height + COffset);			
					vColumns++;
					if (vColumns >= FImagesPerRow)
					{
						vColumns = 0;
						vRows++;
					}
					vLastImage = vImage;
				}
			}
			
			UpdateAlbums();
			
			if(vLastImage)
				FShift.y = vLastImage.y + vLastImage.height;
			if (FScroller)			
				FScroller.update();			
		}
		
		private function LoadImages():void
		{	
			for (var i in FImages)							
				FImages[i].addEventListener(ImageEvent.IMAGE_LOADED, OnImageLoaded);
			ProcessNextImage();
		}
		
		private function ProcessNextImage():Boolean 
		{
			var vResult:ImageContainer = null;
			for (var i in FImages)
			{
				if (!FImages[i].IsLoaded)
				{					
					vResult = FImages[i];					
					break;
				}
			}
			if (vResult)
			{
				vResult.Load();				
				return true;
			}
			else
				return false;
		}
		
		private function OnImageLoaded(e:ImageEvent):void
		{
			e.target.removeEventListener(ImageEvent.IMAGE_LOADED, OnImageLoaded);
			if (FLoadingMessage)
			{
				FCurrent++;
				FLoadingMessage.Description = 'Пожалуйста, подождите, идет загрзука изображений.' + '\n' +
				'Загружено ' + FCurrent + ' из ' + FTotal + '.';
			}
			if (!ProcessNextImage())			
			{				
				AllButtonsEnabled = true;
				FRoot.MessagesCore.visible = false;
			}
			e.Image.addEventListener(MouseEvent.MOUSE_DOWN, OnImageClick);
			e.Image.addEventListener(MouseEvent.MOUSE_OVER, OnImageMouseOver);
			e.Image.addEventListener(ImageEvent.DELETER_OVER, OnDeleterMouseOver);
			e.Image.addEventListener(ImageEvent.DELETER_OUT, OnDeleterMouseOut);
			e.Image.addEventListener(MouseEvent.MOUSE_OUT, OnImageMouseOut);
			e.Image.addEventListener(MouseEvent.DOUBLE_CLICK, OnImageDoubleClick);
			if(FScroller)
				FScroller.update();				
		}
		
		//} endregion
		// End of Local images loading
		
		//{ Images manipulation
		
		public function RemoveImage(AImage:ImageContainer):void
		{
			if (AImage.parent == FContainer)	
			{
				AImage.Destroy();				
				for (var i in FImages)
				{
					if (AImage == FImages[i])
					{
						FImages.splice(i, 1);						
						break;
					}
				}				
				PlaceImages();
			}
		}	
			
		public function ClearSelection():void
		{			
			for (var i in FSelection)
				FSelection[i].Selected = false;
			FSelection = new Vector.<ImageContainer>;			
		}
		
		private function OnStageMouseDown(e:MouseEvent):void 
		{	
			var vDoClear:Boolean = false;
			if (e.target is Stage)
				vDoClear = true;
			else
			{
				if (e.target.parent)
					if (e.target.parent.parent is ScrollPane)
						vDoClear = true;
			}
			if(vDoClear)
				ClearSelection();
			if (e.stageY > FMinSelectionY && e.stageX < Width)
			{
				var vClasses:Boolean = false;				
				for (var i in FRestrictedClasses)				
				{
					if (e.target is FRestrictedClasses[i])
					{
						vClasses = true;
						break;
					}	
				}				
				//if (!vClasses && !(e.target.parent is ImageDescriptor))
				//{					
					//ClearSelection();
				//}
			}
		}
		
		private function OnStageMouseUp(e:MouseEvent):void 
		{
			if (e && FDragContainer.Image)
			{
				if (e.target is ImageContainer)
				{
					if (FDragContainer.Image && FDragContainer.Image != e.target)					
						SwapImages(FDragContainer.Image, ImageContainer(e.target));					
				}
				else if(e.target is AlbumIcon)
				{
					var vAlbum:Albums.AlbumIcon = AlbumIcon(e.target);
					var vAlbumID:int = vAlbum.AlbumRoom.ID;
					if (!vAlbum.IsSelected && FSelection && FSelection.length > 0)
					{
						for (var i in FSelection)						
							FSelection[i].ObjectID = vAlbumID;						
						
						for (var i in FImages)			
						{
							if (FImages[i].ObjectID == vAlbumID)
							{
								UpdateAlbums(FImages[i]);
								trace('Updating album ' + vAlbumID + ' with image ' + FImages[i].ID);
								break;
							}
						}
						var vImages:Vector.<ImageContainer> = ImagesByOwner(vAlbumID);
						UpdateImages(CurrentImages);
						UpdateImages(vImages);						
						ClearSelection();						
						ResetImages();
						PlaceImages();
						UpdateAlbums();
						if(vImages.length > 0)
							FAlbums.UpdateAlbum(vAlbumID, vImages[0]);
						FUploader.Sync();
					}										
				}
			}
			FDragContainer.Image = null;						
		}
		
		private function SwapImages(AFirst:ImageContainer, ASecond:ImageContainer):void 
		{
			var vX:Number = AFirst.x;
			var vY:Number = AFirst.y;					
			AFirst.x = ASecond.x;
			AFirst.y = ASecond.y;
			ASecond.x = vX;
			ASecond.y = vY;
			FContainer.swapChildren(ASecond, AFirst);					
			var vInsertID:int = FImages.indexOf(ASecond);
			var vOriginID:int = FImages.indexOf(AFirst);					
			FImages[vInsertID] = AFirst;
			FImages[vOriginID] = ASecond;
			
			AFirst.Order = FContainer.getChildIndex(AFirst) + 1;
			ASecond.Order = FContainer.getChildIndex(ASecond) + 1;
			
			UpdateAlbums();
		}
		
		private function UpdateImages(AImages:Vector.<ImageContainer> ):void
		{
			for (var i in AImages)
				AImages[i].NotifyUpdate()
		}
		
		public function UpdateAlbums(AImage:ImageContainer = null):void
		{	
			if (!AImage)
			{				
				if (FContainer.numChildren > 0)				
					FAlbums.UpdateTitle(ImageContainer(FContainer.getChildAt(0)))				
				else				
					FAlbums.ClearTitle(FCategory);				
			}
			else
				FAlbums.UpdateTitle(ImageContainer(AImage));
		}
		
		private function OnStageMouseLeave(e:Event):void 
		{			
			OnStageMouseUp(null);			
		}
		
		private function OnMouseMove(e:MouseEvent = null):void 
		{
			if (FDragContainer.DragItem)
			{
				FDragContainer.DragItem.visible = e.target != FDragContainer.Image;
				FTip.visible = false;
				FDragContainer.DragItem.x = FDragContainer.mouseX - FDragContainer.DragItem.width / 2;
				FDragContainer.DragItem.y = FDragContainer.mouseY - FDragContainer.DragItem.height / 2;
			}
		}
		
		private function OnImageClick(e:MouseEvent):void 
		{
			var vImage:ImageContainer;
			if (e.target is ImageContainer)			
				vImage = ImageContainer(e.target)			
			//else if (e.target is TextField)
			//	vImage = ImageContainer(e.target.parent.parent)
			else if (e.target is ImageDescriptor)
				vImage = ImageContainer(e.target.parent);
			stage.focus = InteractiveObject(e.target);
			if (vImage)
				SelectImage(vImage);			
		}
		
		public function SelectImage(AImage:ImageContainer):void
		{
			var vControl:Boolean = KeysUtil.IsKeyDown(Keyboard.CONTROL);
			var vShift:Boolean = !vControl && KeysUtil.IsKeyDown(Keyboard.SHIFT);			
			var vLowIndex:int = LowestIndex;
			var vHighIndex:int = HighestIndex;
			var vIndex:int = 0;			
			if (!AImage.Selected)
			{
				if(!vShift && !vControl && FSelection.length < 2)
					ClearSelection();
				
				if(vShift)
				{					
					var vImageIndex:int = FContainer.getChildIndex(AImage);
					if (vImageIndex > HighestIndex)
						SelectImages(vHighIndex, vImageIndex + 1)
					else if (vImageIndex < vLowIndex)
						SelectImages(vImageIndex, vLowIndex + 1)						
					else
					{
						var vd1 = vImageIndex - vLowIndex;
						var vd2 = vHighIndex - vImageIndex;
						if (vd1 > vd2)
							SelectImages(vLowIndex, vImageIndex + 1);							
						else
							SelectImages(vImageIndex, vHighIndex + 1);						
					}
				}
				else if (vControl || (FSelection.length < 1))
				{
					AImage.Selected = true;	
					FDragContainer.Image = AImage;	
					AddToSelection(AImage);
				}
			}
			else
			{
				if (vControl || FSelection.length < 2)
				{					
					AImage.Selected = false;
					DropSelection(AImage);
				}
				else
					FDragContainer.Image = AImage;
			}
			if(FDragContainer.DragItem)
				FDragContainer.DragItem.visible = false;
		}
		
		public function SelectImages(AFirst:int, ASecond:int):void 
		{
			for (var i:int = AFirst; i < ASecond; i++)
			{						 
				ImageContainer(FContainer.getChildAt(i)).Selected = true;
				AddToSelection(ImageContainer(FContainer.getChildAt(i)));
			}			
		}
		
		public function AddToSelection(AImage:ImageContainer):Boolean
		{
			var vResult:Boolean = false;
			var vIndex:int = FSelection.indexOf(AImage);
			if (vIndex < 0)
			{
				vResult = true;
				FSelection.push(AImage);				
			}
			return vResult;
		}
		
		public function DropSelection(AImage:ImageContainer):void
		{
			if(FDragContainer.Image == AImage)
				FDragContainer.Image = null;
			var vIndex:int = FSelection.indexOf(AImage);
			if (vIndex > -1)
				FSelection.splice(vIndex, 1);
			AImage.Selected = false;
		}		
		
		private function get HighestIndex():int
		{
			var vResult:int = -1;
			var vIndex:int;
			if (FSelection.length == 1)
				return FContainer.getChildIndex(FSelection[0]);
			for (var i in FSelection)
			{
				vIndex = FContainer.getChildIndex(FSelection[i]);
				if (vIndex > vResult)				
					vResult = vIndex;
			}
			return vResult;
		}
		
		private function get LowestIndex():int
		{
			var vResult:int = FContainer.numChildren;
			var vIndex:int;
			for (var i in FSelection)
			{
				vIndex = FContainer.getChildIndex(FSelection[i]);
				if (vIndex < vResult)				
					vResult = vIndex;
			}
			return vResult;
		}
		
		private function OnImageMouseOver(e:MouseEvent):void 
		{
			if (!FDragContainer.DragItem)
			{
				if (e.target is ImageContainer)
				{					
					FTip.Text = ImageContainer(e.target).TipText;					
					FTip.visible = true;
				}
			}
		}
		
		private function OnDeleterMouseOver(e:ImageEvent):void 
		{
			if (!FDragContainer.DragItem)
			{
				if (e.target is ImageContainer)
				{					
					FTip.Text = ImageContainer(e.target).DeleteText;
					FTip.visible = true;
				}
			}
		}
		
		private function OnImageDoubleClick(e:MouseEvent):void 
		{			
			if (FSelection.length < 2)
				SelectImage(ImageContainer(e.target));
			FDragContainer.Image = null;
			//ClearSelection();
		}
		
		private function OnImageMouseOut(e:MouseEvent):void 
		{
			FTip.visible = false;
		}
		
		private function OnDeleterMouseOut(e:ImageEvent):void 
		{
			FTip.visible = false;
		}
		
		public function ImagesByOwner(AID:int):Vector.<ImageContainer>
		{
			var vResult:Vector.<ImageContainer> = new Vector.<ImageContainer>;
			for (var i in FImages)
			{
				if (FImages[i].ObjectID == AID)
					vResult.push(FImages[i]);
			}			
			return vResult;
		}
		
		public function TransferToMain(AID:int):void
		{
			if (AID != FLoader.MainID)
			{
				var vImages:Vector.<ImageContainer> = ImagesByOwner(AID);
				if (vImages.length > 0)
				{				
					for each(var vImage:ImageContainer in vImages)
					{
						vImage.ObjectID = FLoader.MainID;
						vImage.OwnerType = Constants.OWNER_MAIN;
					}
					ClearSelection();
					ResetImages();				
					PlaceImages();					
					FAlbums.ClearTitle(AID);
				}
			}
		}
		
		//} endregion
		// End of Images manipulation
		
		//{ Buttons
		
		public function set Buttons(AButtons:Dictionary):void
		{
			FButtons = AButtons;
		}		
		
		private function ButtonAt(AName:String):IconButton
		{
			return IconButton(FButtons[AName]);
		}		
		
		public function set AllButtonsEnabled(AEnabled:Boolean):void
		{
			ButtonAt('Browse').Enabled = AEnabled;			
			ButtonAt('Update').Enabled = AEnabled;				
		}
		
		//} endregion
		// End of Buttons
		
		//{ Getters & Setters		
		
		public function get Uploader():ImagesUploader 
		{
			return FUploader;
		}
		
		public function get Width():int
		{
			return FWidth;
		}
		
		public function set Width(AWidth:int):void
		{
			FWidth = AWidth;
			FImagesPerRow = Math.floor((FWidth - COffset) / (CImageWidth + COffset));
			PlaceImages();
		}
		
		public function get Height():int
		{
			return FHeight;
		}
		
		public function set Height(AHeight:int):void
		{
			FHeight = AHeight;
		}		
		
		public function set Root(ARoot:Main):void 
		{
			FRoot = ARoot;
			FUploader.Container = this;
			FRoot.addChild(FDragContainer);
		}
		
		public function get Root():Main 
		{
			return FRoot;			
		}
		
		public function set XMLS(ALoader:XMLLoader):void 
		{
			FLoader = ALoader;
			FLoader.addEventListener(Event.CONNECT, OnLoadingStarted);
			FLoader.addEventListener(Event.COMPLETE, OnXMLLoaded);
		}
		
		public function get XMLS():XMLLoader
		{
			return FLoader;
		}
		
		public function set Albums(AAlbums:AlbumsPage):void 
		{
			FAlbums = AAlbums;
		}
		
		public function get CurrentImages():Vector.<ImageContainer>
		{
			return ImagesByOwner(FCategory);
		}
		
		public function get Category():int
		{
			return FCategory;
		}
		
		public function set Category(ACategory:int):void 
		{
			ClearSelection();
			FCategory = ACategory;
			ResetImages();
			PlaceImages();
			if(FScroller)
				FScroller.verticalScrollPosition = 0;
		}
		//} endregion
		// End of Getters & Setters
	}	
}
